home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / MacMud / Mud 4.0 / backend.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-24  |  22.6 KB  |  927 lines  |  [TEXT/MPS ]

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <signal.h>
  4. #include <setjmp.h>
  5. #include <ctype.h>
  6. #include <sys/types.h>
  7. #include <types.h>
  8. #include <cursorctl.h>
  9. #include <desk.h>
  10. #include <mac.h>
  11. #include <fcntl.h>
  12. #include <math.h>
  13. #include <memory.h>
  14.  
  15. #include "lint.h"
  16. #include "config.h"
  17. #include "interpret.h"
  18. #include "object.h"
  19. #include "wiz_list.h"
  20. #include "exec.h"
  21. #include "comm.h"
  22. #include "rc.h"
  23.  
  24. jmp_buf error_recovery_context;
  25. int error_recovery_context_exists = 0;
  26. /*
  27.  * The 'current_time' is updated at every heart beat.
  28.  */
  29. int current_time;
  30.  
  31. extern long times PROT((struct tms *));
  32. extern int fstat PROT((int, struct stat *));
  33. extern int stat PROT((char *, struct stat *));
  34.  
  35. static void cycle_hb_list PROT((void));
  36. extern struct object *command_giver, *current_interactive, *obj_list_destruct;
  37. extern int num_player, d_flag;
  38. extern struct object *previous_ob, *master_ob;
  39.  
  40. struct object *current_heart_beat;
  41.  
  42. void call_heart_beat(), catch_alarm();
  43. void load_first_objects(), prepare_ipc(),
  44.     shutdowngame(), ed_cmd PROT((char *)),
  45.     print_prompt(), call_out(),
  46.     destruct2 PROT((struct object *));
  47.  
  48. extern int get_message PROT((char *, int)), player_parser PROT((char *)),
  49.     call_function_interactive PROT((struct interactive *, char *)),
  50.     resort_free_list(), swap PROT((struct object *));
  51.  
  52. extern void flush_all_player_mess();
  53.  
  54. extern int t_flag;
  55. int time_to_call_heart_beat;
  56. int comm_time_to_call_heart_beat = 0; /* this is set by interrupt, */
  57.     /* comm sets time_to_call_heart_beat sometime after */
  58.  
  59. /*
  60.  * There are global variables that must be zeroed before any execution.
  61.  * In case of errors, there will be a longjmp(), and the variables will
  62.  * have to be cleared explicitely. They are normally maintained by the
  63.  * code that use them.
  64.  *
  65.  * This routine must only be called from top level, not from inside
  66.  * stack machine execution (as stack will be cleared).
  67.  */
  68. void clear_state() {
  69.     extern struct object *previous_ob;
  70.  
  71.     current_object = 0;
  72.     command_giver = 0;
  73.     current_interactive = 0;
  74.     previous_ob = 0;
  75.     current_prog = 0;
  76.     error_recovery_context_exists = 1;
  77.     reset_machine(0);    /* Pop down the stack. */
  78. }
  79.  
  80. void logon(ob)
  81.     struct object *ob;
  82. {
  83.     struct svalue *ret;
  84.     struct object *save = current_object;
  85.  
  86.     /*
  87.      * current_object must be set here, so that the static "logon" in
  88.      * player.c can be called.
  89.      */
  90.     current_object = ob;
  91.     ret = apply("logon", ob, 0);
  92.     if (ret == 0) {
  93.     add_message("prog %s:\n", ob->name);
  94.     fatal("Could not find logon on the player %s\n", ob->name);
  95.     }
  96.     current_object = save;
  97. }
  98.  
  99. /*
  100.  * Take a player command and parse it.
  101.  * The command can also come from a NPC.
  102.  * Beware that 'str' can be modified and extended !
  103.  */
  104. int parse_command(str, ob)
  105.     char *str;
  106.     struct object *ob;
  107. {
  108.     struct object *save = command_giver;
  109.     int res;
  110.  
  111.     command_giver = ob;
  112.     res = player_parser(str);
  113.     command_giver = save;
  114.     return res;
  115. }
  116.  
  117. /*
  118.  * This is the backend. We will stay here for ever (almost).
  119.  */
  120. int eval_cost;
  121. void backend()
  122. {
  123.     char buff[2000];
  124.     extern int game_is_being_shut_down;
  125.     extern int slow_shut_down_to_do;
  126.  
  127.     prepare_ipc();
  128.  
  129.     if (!t_flag)
  130.     call_heart_beat();
  131.     setjmp(error_recovery_context);
  132.     while(1) {
  133.     /*
  134.      * The call of clear_state() should not really have to be done
  135.      * once every loop. However, there seem to be holes where the
  136.      * state is not consistent. If these holes are removed,
  137.      * then the call of clear_state() can be moved to just before the
  138.      * while() - statment. *sigh* /Lars
  139.      */
  140.     clear_state();
  141.     eval_cost = 0;
  142.     remove_destructed_objects(); /* marion - before ref checks! */
  143. #ifdef DEBUG
  144.     if (d_flag > 1)
  145.         check_a_lot_ref_counts(0);
  146. #endif
  147.     if (game_is_being_shut_down)
  148.         shutdowngame();
  149.     if (slow_shut_down_to_do) {
  150.         int tmp = slow_shut_down_to_do;
  151.         slow_shut_down_to_do = 0;
  152.         slow_shut_down(tmp);
  153.     }
  154.  
  155.     if (get_message(buff, sizeof buff)) {
  156.         void update_load_av PROT((void));
  157.  
  158.         update_load_av();
  159.         /*
  160.          * Now we have a string from the player. This string can go to
  161.          * one of several places. If it is prepended with a '!', then
  162.          * it is an escape from the 'ed' editor, so we send it
  163.          * as a command to the parser.
  164.          * If any object function is waiting for an input string, then
  165.          * send it there.
  166.          * Otherwise, send the string to the parser.
  167.          * The player_parser() will find that current_object is 0, and
  168.          * then set current_object to point to the object that defines
  169.          * the command. This will enable such functions to be static.
  170.          */
  171.         current_object = 0;
  172.         current_interactive = command_giver;
  173. #ifdef DEBUG
  174.         if (!command_giver->interactive)
  175.         fatal("Non interactive player in main loop !\n");
  176. #endif
  177.         if (buff[0] == '!' && command_giver->super)
  178.         parse_command(buff+1, command_giver);
  179.         else if (command_giver->interactive->ed_buffer)
  180.         ed_cmd(buff);
  181.         else if (call_function_interactive(command_giver->interactive,buff))
  182.         ;    /* Do nothing ! */
  183.         else
  184.         parse_command(buff, command_giver);
  185.         /*
  186.          * Print a prompt if player is still here.
  187.          */
  188.         if (command_giver->interactive)
  189.         print_prompt();
  190.     }
  191.  
  192.     if (time_to_call_heart_beat)
  193.         call_heart_beat();
  194.     command_giver = 0;
  195.     }
  196. }
  197.  
  198. /*
  199.  * Despite the name, this routine takes care of several things.
  200.  * It will loop through all objects once every 10 minutes.
  201.  *
  202.  * If an object is found in a state of not having done reset, and the
  203.  * delay to next reset has passed, then reset() will be done.
  204.  *
  205.  * If the object has a existed more than the time limit given for swapping,
  206.  * then 'clean_up' will first be called in the object, after which it will
  207.  * be swapped out if it still exists.
  208.  *
  209.  * There are some problems if the object self-destructs in clean_up, so
  210.  * special care has to be taken of how the linked list is used.
  211.  */
  212. static void look_for_objects_to_swap() {
  213.     extern long time_to_swap; /* marion - for invocation parameter */
  214.     static int next_time;
  215.     struct object *ob;
  216.     struct object *next_ob;
  217.     jmp_buf save_error_recovery_context;
  218.     int save_rec_exists;
  219.  
  220.     if (current_time < next_time)
  221.     return;                /* Not time to look yet */
  222.     next_time = current_time + 15 * 60;    /* Next time is in 15 minutes */
  223.     memcpy((char *) save_error_recovery_context,
  224.        (char *) error_recovery_context, sizeof error_recovery_context);
  225.     save_rec_exists = error_recovery_context_exists;
  226.     /*
  227.      * Objects object can be destructed, which means that
  228.      * next object to investigate is saved in next_ob. If very unlucky,
  229.      * that object can be destructed too. In that case, the loop is simply
  230.      * restarted.
  231.      */
  232.     for (ob = obj_list; ob; ob = next_ob) {
  233.     int ready_for_swap;
  234.     if (ob->flags & O_DESTRUCTED) {
  235.         ob = obj_list; /* restart */
  236.     }
  237.     next_ob = ob->next_all;
  238.         if (setjmp(error_recovery_context)) {        /* amylaar */
  239.             extern void clear_state();
  240.             clear_state();
  241.             debug_message("Error in look_for_objects_to_swap.\n");
  242.         continue;
  243.         }
  244.     /*
  245.      * Check reference time before reset() is called.
  246.      */
  247.     if (current_time < ob->time_of_ref + time_to_swap)
  248.         ready_for_swap = 0;
  249.     else
  250.         ready_for_swap = 1;
  251.     /*
  252.      * Should this object have reset(1) called ?
  253.      */
  254.     if (ob->next_reset < current_time && !(ob->flags & O_RESET_STATE)) {
  255.         if (d_flag)
  256.         fprintf(stderr, "RESET %s\n", ob->name);
  257.         reset_object(ob, 1);
  258.     }
  259. #ifdef TIME_TO_CLEAN_UP
  260.     /*
  261.      * Has enough time passed, to give the object a chance
  262.      * to self-destruct ? Save the O_RESET_STATE, which will be cleared.
  263.      *
  264.      * Only call clean_up in objects that has defined such a function.
  265.      *
  266.      * Only if the clean_up returns a non-zero value, will it be called
  267.      * again.
  268.      */
  269.     if (current_time - ob->time_of_ref > TIME_TO_CLEAN_UP &&
  270.         (ob->flags & O_WILL_CLEAN_UP))
  271.     {
  272.         int save_reset_state = ob->flags & O_RESET_STATE;
  273.         struct svalue *svp;
  274.  
  275.         if (d_flag)
  276.         fprintf(stderr, "clean up %s\n", ob->name);
  277.         /*
  278.          * Supply a flag to the object that says if this program
  279.          * is inherited by other objects. Cloned objects might as well
  280.          * believe they are not inherited. Swapped objects will not
  281.          * have a ref count > 1 (and will have an invalid ob->prog
  282.          * pointer).
  283.          */
  284.         push_number(ob->flags & (O_CLONE|O_SWAPPED) ? 0 : ob->prog->ref);
  285.         svp = apply("clean_up", ob, 1);
  286.         if (ob->flags & O_DESTRUCTED)
  287.         continue;
  288.         if (!svp || (svp->type == T_NUMBER && svp->u.number == 0))
  289.         ob->flags &= ~O_WILL_CLEAN_UP;
  290.         ob->flags |= save_reset_state;
  291.     }
  292. #endif /* TIME_TO_CLEAN_UP > 0 */
  293. #ifdef TIME_TO_SWAP
  294.     /*
  295.      * At last, there is a possibility that the object can be swapped
  296.      * out.
  297.      */
  298.     if (ob->flags & O_SWAPPED || !ready_for_swap)
  299.         continue;
  300.     if (ob->flags & O_HEART_BEAT)
  301.         continue;
  302.     if (d_flag)
  303.         fprintf(stderr, "swap %s\n", ob->name);
  304.     swap(ob);    /* See if it is possible to swap out to disk */
  305. #endif
  306.     }
  307.     memcpy((char *) error_recovery_context,
  308.        (char *) save_error_recovery_context,
  309.        sizeof error_recovery_context);
  310.     error_recovery_context_exists = save_rec_exists;
  311. }
  312.  
  313. /*
  314.  * Call all heart_beat() functions in all objects.  Also call the next reset,
  315.  * and the call out.
  316.  * We do heart beats by moving each object done to the end of the heart beat
  317.  * list before we call its function, and always using the item at the head
  318.  * of the list as our function to call.  We keep calling heart beats until
  319.  * a timeout or we have done num_heart_objs calls.  It is done this way so
  320.  * that objects can delete heart beating objects from the list from within
  321.  * their heart beat without truncating the current round of heart beats.
  322.  *
  323.  * Set command_giver to current_object if it is a living object. If the object
  324.  * is shadowed, check the shadowed object if living. There is no need to save
  325.  * the value of the command_giver, as the caller resets it to 0 anyway.
  326.  */
  327. static struct object * hb_list = 0; /* head */
  328. static struct object * hb_tail = 0; /* for sane wrap around */
  329.  
  330. static int num_hb_objs = 0;  /* so we know when to stop! */
  331. static int num_hb_calls = 0; /* stats */
  332. static float perc_hb_probes = 100.0; /* decaying avge of how many complete */
  333.  
  334. void call_heart_beat() {
  335.     struct object *ob, *hide_current = current_object;
  336.     int num_done = 0;
  337.     
  338.     time_to_call_heart_beat = 0; /* interrupt loop if we take too long */
  339.     comm_time_to_call_heart_beat = 0;
  340.  
  341.     start_timer(2);
  342.     current_time = get_current_time();
  343.     current_interactive = 0;
  344.  
  345.     if ((num_player > 0) && hb_list) {
  346.         num_hb_calls++;
  347.     while (hb_list && !comm_time_to_call_heart_beat  && (num_done < num_hb_objs)) {
  348.         num_done++;
  349.         cycle_hb_list();
  350.         ob = hb_tail; /* now at end */
  351.         if (!(ob->flags & O_HEART_BEAT))
  352.             fatal("Heart beat not set in object on heart beat list!");
  353.         if (ob->flags & O_SWAPPED)
  354.             fatal("Heart beat in swapped object.\n");
  355.         /* move ob to end of list, do ob */
  356.          if (ob->prog->heart_beat == -1)
  357.           continue;
  358.          current_prog = ob->prog;
  359.         current_object = ob;
  360.         current_heart_beat = ob;
  361.         command_giver = ob;
  362.         while(command_giver->shadowing)
  363.             command_giver = command_giver->shadowing;
  364.         if (!(command_giver->flags & O_ENABLE_COMMANDS))
  365.             command_giver = 0;
  366.         if (ob->user)
  367.             ob->user->heart_beats++;
  368.         eval_cost = 0;
  369.          call_function(ob->prog,
  370.                &ob->prog->functions[ob->prog->heart_beat]);
  371.     }
  372.     if (num_hb_objs)
  373.         perc_hb_probes = 100 * (float) num_done / num_hb_objs;
  374.     else
  375.         perc_hb_probes = 100.0;
  376.     }
  377.     command_giver = 0;
  378.     current_object = hide_current;
  379.     current_heart_beat = 0;
  380.     look_for_objects_to_swap();
  381.     call_out();    /* some things depend on this, even without players! */
  382.     flush_all_player_mess();
  383.     wiz_decay();
  384. }
  385.  
  386. /*
  387.  * Take the first object off the heart beat list, place it at the end
  388.  */
  389. static void cycle_hb_list()
  390. {
  391.     struct object * ob;
  392.  
  393.     if (!hb_list)
  394.         fatal("Cycle heart beat list with empty list!");
  395.     if (hb_list == hb_tail)
  396.         return; /* 1 object on list */
  397.     ob = hb_list;
  398.     hb_list = hb_list -> next_heart_beat;
  399.     hb_tail -> next_heart_beat = ob;
  400.     hb_tail = ob;
  401.     ob->next_heart_beat = 0;
  402. }
  403.  
  404. /*
  405.  * add or remove an object from the heart beat list; does the major check...
  406.  * If an object removes something from the list from within a heart beat,
  407.  * various pointers in call_heart_beat could be stuffed, so we must
  408.  * check current_heart_beat and adjust pointers.
  409.  */
  410.  
  411. int set_heart_beat(ob, to)
  412.     struct object * ob;
  413.     int to;
  414. {
  415.     struct object * o = hb_list;
  416.     struct object * oprev = 0;
  417.  
  418.     if (ob->flags & O_DESTRUCTED)
  419.         return 0;
  420.     if (to)
  421.         to = 1;
  422.  
  423.     while (o && o != ob) {
  424.         if (!(o->flags & O_HEART_BEAT))
  425.             fatal("Found disabled object in the active heart beat list!\n");
  426.         oprev = o;
  427.         o = o->next_heart_beat;
  428.     }
  429.  
  430.     if (!o && (ob->flags & O_HEART_BEAT))
  431.         fatal("Couldn't find enabled object in heart beat list!");
  432.     
  433.     if (to == ((ob->flags & O_HEART_BEAT) != 0))
  434.         return(0);
  435.  
  436.     if (to) {
  437.         ob->flags |= O_HEART_BEAT;
  438.         if (ob->next_heart_beat)
  439.             fatal("Dangling pointer to next_heart_beat in object!");
  440.         ob->next_heart_beat = hb_list;
  441.         hb_list = ob;
  442.         if (!hb_tail) hb_tail = ob;
  443.             num_hb_objs++;
  444.         cycle_hb_list();     /* Added by Linus. 911104 */
  445.     } else { /* remove all refs */
  446.         ob->flags &= ~O_HEART_BEAT;
  447.         if (hb_list == ob)
  448.             hb_list = ob->next_heart_beat;
  449.         if (hb_tail == ob)
  450.             hb_tail = oprev;
  451.         if (oprev)
  452.             oprev->next_heart_beat = ob->next_heart_beat;
  453.         ob->next_heart_beat = 0;
  454.         num_hb_objs--;
  455.     }
  456.     return(1);
  457. }
  458. /*
  459.  * sigh.  Another status function.
  460.  */
  461. int heart_beat_status(verbose)
  462.     int verbose;
  463. {
  464.     char buf[20];
  465.  
  466.     if (verbose) {
  467.         add_message("\nHeart beat information:\n");
  468.         add_message("-----------------------\n");
  469.         add_message("Number of objects with heart beat: %d, starts: %d\n",
  470.                 num_hb_objs, num_hb_calls);
  471.         sprintf(buf, "%.2f", perc_hb_probes);
  472.         add_message("Percentage of HB calls completed last time: %s\n", buf);
  473.     }
  474.     return 0;
  475. }
  476.  
  477. /*
  478.  * There is a file with a list of objects to be initialized at
  479.  * start up.
  480.  */
  481.  
  482. void load_first_objects() { /* Old version used when o_flag true /JnA */
  483.     FILE *f;
  484.     char *p;
  485.     long timer;
  486.     char buff[1000];
  487.     extern int e_flag;
  488.  
  489.     if (e_flag)
  490.         return;
  491.     (void)printf("Loading init file %s\n", INIT_FILE);
  492.     f = fopen(INIT_FILE, "r");
  493.     if (f == 0)
  494.         return;
  495.     if (setjmp(error_recovery_context)) {
  496.         clear_state();
  497.         add_message("Anomaly in the fabric of world space.\n");
  498.     }
  499.     error_recovery_context_exists = 1;
  500.     timer = TickCount();
  501.     while(1) {
  502.         if (fgets(buff, sizeof buff, f) == NULL)
  503.             break;
  504.         if (buff[0] == '#')
  505.             continue;
  506.         p = strchr(buff, '\n');
  507.         if (p != 0)
  508.             *p = 0;
  509.         if (buff[0] == '\0')
  510.             continue;
  511.         (void)printf("Preloading: %s", buff);
  512.         fflush(stdout);
  513.         eval_cost = 0;
  514.         (void)find_object(buff);
  515. #ifdef MALLOC_malloc
  516.         resort_free_list();
  517. #endif
  518.         (void)printf(" %.2f\n", (TickCount() - timer) / 60.0);
  519.         fflush(stdout);
  520.     }
  521.     error_recovery_context_exists = 0;
  522.     fclose(f);
  523. }
  524.  
  525. /*
  526.  * New version used when not in -o mode. The epilog() in master.c is
  527.  * supposed to return an array of files (castles in 2.4.5) to load. The array
  528.  * returned by apply() will be freed at next call of apply(), which means that
  529.  * the ref count has to be incremented to protect against deallocation.
  530.  *
  531.  * The master object is asked to do the actual loading.
  532.  */
  533. void preload_objects(eflag)
  534.     int eflag;
  535. {
  536.     int ix;
  537.     struct svalue *ret;
  538.     struct vector *prefiles;
  539.  
  540.     push_number(eflag);
  541.     ret = apply_master_ob("epilog", 1);
  542.  
  543.     if ((ret == 0) || (ret->type != T_POINTER)) {
  544.         return;
  545.     } else {
  546.         prefiles = ret->u.vec;
  547.     }
  548.  
  549.     if ((prefiles == 0) || (prefiles->size < 1))
  550.         return;
  551.  
  552.     prefiles->ref++;
  553.  
  554.     ix = -1;
  555.     if (setjmp(error_recovery_context)) {
  556.         clear_state();
  557.         add_message("Anomaly in the fabric of world space.\n");
  558.     }
  559.     error_recovery_context_exists = 1;
  560.  
  561.     while (++ix < prefiles->size) {
  562.         if (prefiles->item[ix].type != T_STRING)
  563.             continue;
  564.  
  565.         eval_cost = 0;
  566.         push_string(prefiles->item[ix].u.string, STRING_MALLOC);
  567.         (void)apply_master_ob("preload", 1);
  568.  
  569. #ifdef MALLOC_malloc
  570.         resort_free_list();
  571. #endif
  572.     }
  573.     free_vector(prefiles);
  574.     error_recovery_context_exists = 0;
  575. }
  576.  
  577. /*
  578.  * catch alarm, set flag for comms code and heart_beat to catch.
  579.  * comms code sets time_to_call_heart_beat for the backend when
  580.  * it has completed the current round of player commands.
  581.  */
  582.  
  583. void catch_alarm() {
  584.     comm_time_to_call_heart_beat = 1;
  585. }
  586.  
  587. /*
  588.  * All destructed objects are moved int a sperate linked list,
  589.  * and deallocated after program execution.
  590.  */
  591. void remove_destructed_objects()
  592. {
  593.     struct object *ob, *next;
  594.     for (ob=obj_list_destruct; ob; ob = next) {
  595.         next = ob->next_all;
  596.         destruct2(ob);
  597.     }
  598.     obj_list_destruct = 0;
  599. }
  600. #ifndef NLHACK
  601. /*
  602.  * Append string to file. Return 0 for failure, otherwise 1.
  603.  */
  604. int write_file(file, str)
  605.     char *file;
  606.     char *str;
  607. {
  608.     FILE *f;
  609.  
  610. #ifdef COMPAT_MODE
  611.     file = check_file_name(file, 1);
  612. #else
  613.     file = check_valid_path(file, current_object->eff_user, "write_file", 1);
  614. #endif
  615.     if (!file)
  616.     return 0;
  617.     f = fopen(file, "a");
  618.     if (f == 0)
  619.     error("Wrong permissions for opening file %s for append.\n", file);
  620.     fwrite(str, strlen(str), 1, f);
  621.     fclose(f);
  622.     return 1;
  623. }
  624. #endif
  625.  
  626. char *read_file(file,start,len)
  627.     char *file;
  628.     int start,len;
  629. {
  630.     struct stat st;
  631.     FILE *f;
  632.     char *str,*p,*p2,*end,c;
  633.     int size;
  634.  
  635.     if (len < 0) return 0;
  636. #ifdef COMPAT_MODE
  637.     file = check_file_name(file, 0);
  638. #else    
  639.     file = check_valid_path(file, current_object->eff_user, "read_file", 0);
  640. #endif    
  641.  
  642.     if (!file)
  643.     return 0;
  644.     f = fopen(file, "r");
  645.     if (f == 0)
  646.     return 0;
  647. #ifndef mac
  648.     if (fstat(fileno(f), &st) == -1)
  649. #else
  650.     if (stat(file, &st) == -1)
  651. #endif
  652.     fatal("Could not stat an open file.\n");
  653.     size = st.st_size;
  654.     if (size > READ_FILE_MAX_SIZE) {
  655.     if ( start || len ) size = READ_FILE_MAX_SIZE;
  656.     else {
  657.         fclose(f);
  658.         return 0;
  659.     }
  660.     }
  661.     if (!start) start = 1;
  662.     if (!len) len = READ_FILE_MAX_SIZE;
  663.     str = xalloc(size + 1);
  664.     str[size] = '\0';
  665.     do {
  666.     if (size > st.st_size)
  667.         size = st.st_size;
  668.         if (fread(str, size, 1, f) != 1) {
  669.             fclose(f);
  670.         xfree(str);
  671.             return 0;
  672.         }
  673.     st.st_size -= size;
  674.     end = str+size;
  675.         for (p=str; ( p2=memchr(p,'\n',end-p) ) && --start; ) p=p2+1;
  676.     } while ( start > 1 );
  677.     for (p2=str; p != end; ) {
  678.         c = *p++;
  679.     if ( !isprint(c) && !isspace(c) ) c=' ';
  680.     *p2++=c;
  681.     if ( c == '\n' )
  682.         if (!--len) break;
  683.     }
  684.     if ( len && st.st_size ) {
  685.     size -= ( p2-str) ; 
  686.     if (size > st.st_size)
  687.         size = st.st_size;
  688.         if (fread(p2, size, 1, f) != 1) {
  689.             fclose(f);
  690.         xfree(str);
  691.             return 0;
  692.         }
  693.     st.st_size -= size;
  694.     end = p2+size;
  695.         for (; p2 != end; ) {
  696.         c = *p2;
  697.         if ( !isprint(c) && !isspace(c) ) *p2=' ';
  698.         p2++;
  699.         if ( c == '\n' )
  700.             if (!--len) break;
  701.     }
  702.     if ( st.st_size && len ) {
  703.         /* tried to read more than READ_MAX_FILE_SIZE */
  704.         fclose(f);
  705.         xfree(str);
  706.         return 0;
  707.     }
  708.     }
  709.     *p2='\0';
  710.     fclose(f);
  711. #if 0 /* caller immediately frees the string again,
  712.        * so there's no use to make it smaller now
  713.        */
  714.     if ( st.st_size > (p2-str) ) {
  715. /* can't allocate shared string when string type isn't passed to the caller */
  716.     p2=strdup(str);
  717.     xfree(str);
  718.     return p2;
  719.     }
  720. #endif
  721.     return str;
  722. }
  723.  
  724.  
  725. char *read_bytes(file,start,len)
  726.     char *file;
  727.     int start,len;
  728. {
  729.     struct stat st;
  730.  
  731.     char *str,*p;
  732.     int size, f;
  733.     int lseek();
  734.  
  735.     if (len < 0)
  736.     return 0;
  737.     if(len > MAX_BYTE_TRANSFER)
  738.     return 0;
  739. #ifdef COMPAT_MODE
  740.     file = check_file_name(file, 0);
  741. #else    
  742.     file = check_valid_path(file, current_object->eff_user, 
  743.                 "read_bytes", 0);
  744. #endif    
  745.  
  746.     if (!file)
  747.     return 0;
  748.     f = open(file, O_RDONLY);
  749.     if (f < 0)
  750.     return 0;
  751.  
  752. #ifndef mac
  753.     if (fstat(f, &st) == -1)
  754. #else
  755.     if (stat(file, &st) == -1)
  756. #endif
  757.     fatal("Could not stat an open file.\n");
  758.     size = st.st_size;
  759.     if(start < 0) 
  760.     start = size + start;
  761.  
  762.     if (start >= size) {
  763.     close(f);
  764.     return 0;
  765.     }
  766.     if ((start+len) > size) 
  767.     len = (size - start);
  768.  
  769.     if ((size = lseek(f,start, 0)) < 0)
  770.     return 0;
  771.  
  772.     str = (char *)alloca(len + 1);
  773.  
  774.     size = read(f, str, len);
  775.  
  776.     close(f);
  777.  
  778.     if (size <= 0)
  779.     return 0;
  780.  
  781.     /* We want to allow all characters to pass untouched!
  782.     for (il=0;il<size;il++) 
  783.     if (!isprint(str[il]) && !isspace(str[il]))
  784.         str[il] = ' ';
  785.  
  786.     str[il] = 0;
  787.     */
  788.     /*
  789.      * The string has to end to '\0'!!!
  790.      */
  791.     str[size] = '\0';
  792.  
  793.     p = string_copy(str);
  794.  
  795.     return p;
  796. }
  797.  
  798. int write_bytes(file,start,str)
  799.     char *file, *str;
  800.     int start;
  801. {
  802.     struct stat st;
  803.  
  804.     int size, f;
  805.     int lseek();
  806.  
  807. #ifdef COMPAT_MODE    
  808.     file = check_file_name(file, 1);
  809. #else    
  810.     file = check_valid_path(file, current_object->eff_user, 
  811.                 "write_bytes", 1);
  812. #endif    
  813.  
  814.     if (!file)
  815.     return 0;
  816.     if(strlen(str) > MAX_BYTE_TRANSFER)
  817.     return 0;
  818.     f = open(file, O_WRONLY);
  819.     if (f < 0)
  820.     return 0;
  821.  
  822. #ifndef mac
  823.     if (fstat(f, &st) == -1)
  824. #else
  825.     if (stat(file, &st) == -1)
  826. #endif
  827.     fatal("Could not stat an open file.\n");
  828.     size = st.st_size;
  829.     if(start < 0) 
  830.     start = size + start;
  831.  
  832.     if (start >= size) {
  833.     close(f);
  834.     return 0;
  835.     }
  836.     if ((start+strlen(str)) > size) 
  837.     return 0;
  838.  
  839.     if ((size = lseek(f,start, 0)) < 0)
  840.     return 0;
  841.  
  842.     size = write(f, str, strlen(str));
  843.  
  844.     close(f);
  845.  
  846.     if (size <= 0) {
  847.     return 0;
  848.     }
  849.  
  850.     return 1;
  851. }
  852.  
  853.  
  854. int file_size(file)
  855.     char *file;
  856. {
  857.     struct stat st;
  858.  
  859. #ifdef COMPAT_MODE
  860.     file = check_file_name(file, 0);
  861. #else
  862.     file = check_valid_path(file, current_object->eff_user, "file_size", 0);
  863. #endif
  864.     if (!file)
  865.     return -1;
  866.     if (stat(file, &st) == -1)
  867.     return -1;
  868.     if (S_IFDIR & st.st_mode)
  869.     return -2;
  870.     return st.st_size;
  871. }
  872.  
  873. static double load_av = 0.0;
  874.  
  875. void update_load_av() {
  876.     extern double consts[5];
  877.     extern int current_time;
  878.     static int last_time;
  879.     int n;
  880.     double c;
  881.     static int acc = 0;
  882.  
  883.     acc++;
  884.     if (current_time == last_time)
  885.     return;
  886.     n = current_time - last_time;
  887.     if (n < sizeof consts / sizeof consts[0])
  888.     c = consts[n];
  889.     else
  890.     c = exp(- n / 900.0);
  891.     load_av = c * load_av + acc * (1 - c) / n;
  892.     last_time = current_time;
  893.     acc = 0;
  894. }
  895.  
  896. static double compile_av = 0.0;
  897.  
  898. void update_compile_av(lines)
  899.     int lines;
  900. {
  901.     extern double consts[5];
  902.     extern int current_time;
  903.     static int last_time;
  904.     int n;
  905.     double c;
  906.     static int acc = 0;
  907.  
  908.     acc += lines;
  909.     if (current_time == last_time)
  910.     return;
  911.     n = current_time - last_time;
  912.     if (n < sizeof consts / sizeof consts[0])
  913.     c = consts[n];
  914.     else
  915.     c = exp(- n / 900.0);
  916.     compile_av = c * compile_av + acc * (1 - c) / n;
  917.     last_time = current_time;
  918.     acc = 0;
  919. }
  920.  
  921. char *query_load_av() {
  922.     static char buff[100];
  923.  
  924.     sprintf(buff, "%.2f cmds/s, %.2f comp lines/s", load_av, compile_av);
  925.     return buff;
  926. }
  927.